package com.yfbao.horizon.utils;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrentUtilsTest {


    private static final Logger log = LoggerFactory.getLogger(com.yfbao.horizon.utils.LockUtils.class);
    private static final Map<String, LockDto> LOCK_MAP = new ConcurrentHashMap();

    public ConcurrentUtilsTest() {
    }

    public static LockDto getLock(String lockName){
        return  LOCK_MAP.get(lockName);
    }

    public static boolean tryLock(String lockName, long waitMillis) {
        boolean locked = false;
        LockDto curLock = null;
        synchronized (LOCK_MAP) {
            curLock = (LockDto) LOCK_MAP.get(lockName);
            if (curLock == null) {
                curLock = new LockDto();
                LOCK_MAP.put(lockName, curLock);
            }
        }
        try {
            if(curLock != null && curLock.isInCache()){
                return locked;
            }
            if (curLock != null ) {
                long l = System.currentTimeMillis();
                locked = curLock.getLock().tryLock(waitMillis, TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName()+" wait time:"+(System.currentTimeMillis()-l)+"--"+locked);
//                locked = curLock.getLock().tryLock();
            }
        } catch (Exception var7) {
            log.error(var7.getMessage());
        }

        if (locked && curLock != null) {
            if(!curLock.isInCache()&& curLock.getLock().getHoldCount()>1){
                try {
                    System.out.println(Thread.currentThread().getName()+":conditon await");
                    curLock.getCondition().await(waitMillis,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+":wait error,thread:" + Thread.currentThread().getName());
                }
                System.out.println(Thread.currentThread().getName()+":conditon await outTime");
            }
        }
        return locked;
    }

    public static boolean tryLockOrCondition(String lockName, long waitMillis) {
        boolean locked = false;
        LockDto curLock = null;
        synchronized (LOCK_MAP) {
            curLock = (LockDto) LOCK_MAP.get(lockName);
            if (curLock == null) {
                curLock = new LockDto();
                LOCK_MAP.put(lockName, curLock);
            }
        }
        try {
            if(curLock != null && curLock.isInCache()){
                return locked;
            }
            if (curLock != null ) {
                long l = System.currentTimeMillis();
                locked = curLock.getLock().tryLock(waitMillis, TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName()+" wait time:"+(System.currentTimeMillis()-l)+"--"+locked);
//                locked = curLock.getLock().tryLock();
            }
        } catch (Exception var7) {
            log.error(var7.getMessage());
        }

        if (locked && curLock != null) {
            if(!curLock.isInCache()&& curLock.getLock().getHoldCount()>1){
                try {
                    System.out.println(Thread.currentThread().getName()+":conditon await");
                    curLock.getCondition().await(waitMillis,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+":wait error,thread:" + Thread.currentThread().getName());
                }
                System.out.println(Thread.currentThread().getName()+":conditon await outTime");
            }
        }
        return locked;
    }

    public static void releaseLockAndNotifyAll(String lockName) {
        synchronized (LOCK_MAP) {
            LockDto locklock = (LockDto) LOCK_MAP.get(lockName);
            if (locklock != null) {
                locklock.getCondition().signalAll();
                locklock.getLock().unlock();
                locklock.setInCache(true);
                if (locklock.getLock().getHoldCount() == 0) {
                    LOCK_MAP.remove(lockName);
                }
            }
        }
    }

    public static boolean getTryProduceLock(String lockName){
        LockDto lockDto = LOCK_MAP.get(lockName);
        if(lockDto ==null){
            synchronized (LOCK_MAP) {
                lockDto = (LockDto) LOCK_MAP.get(lockName);
                if (lockDto == null) {
                    lockDto = new LockDto();
                    LOCK_MAP.put(lockName, lockDto);
                }
            }
        }
        int holdCount = lockDto.getLock().getHoldCount();
        if(holdCount>0){
            return false;
        }
        boolean locked = lockDto.getLock().tryLock();
        return locked;
    }

    public static void spinWait(String lockName,long timeout){
        LockDto lock = getLock(lockName);
        if(lock ==null){
            return;
        }
        long start = System.currentTimeMillis();
        while (!lock.isInCache() && ((System.currentTimeMillis()-start)<timeout)) {
            // 自旋等待，条件未满足时不停地检查
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 条件满足后执行相应操作
    }


    @Data
    public static class LockDto {
        private ReentrantLock lock= new ReentrantLock();
        private Condition condition = lock.newCondition();
        private volatile boolean inCache =false;
    }

}
